package com.zy.je.common.core.utils.lang;

import com.zy.je.common.core.utils.CheckUtil;
import org.dromara.hutool.core.array.ArrayUtil;

import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.List;
import java.util.Random;
import java.util.Stack;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;

/**
 * Number具类
 *
 * @author zy
 */
public class NumberUtil extends org.dromara.hutool.core.math.NumberUtil {

    /**
     * 格式化数值格式，保留指定位小数
     */
    public static String formatToNumber(String val, int scale) {
        return String.valueOf(NumberUtil.round(toBigDecimal(val), scale));
    }

    /**
     * 高精度整数进制转换工具<br>
     * 支2到62进制的之间互转
     */
    private static final String TARGET_STR = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
    private static final char[] chs = TARGET_STR.toCharArray();
    private static final BigInteger INTEGER0 = new BigInteger("0");
    public static final int TEN = 10;

    public static String numToRadix(String number, int radix) {
        BigInteger bigNumber = new BigInteger(number);
        BigInteger bigRadix = new BigInteger(radix + "");

        Stack<Character> stack = new Stack<>();
        StringBuilder result = new StringBuilder(0);
        while (!bigNumber.equals(INTEGER0)) {
            stack.add(chs[bigNumber.remainder(bigRadix).intValue()]);
            bigNumber = bigNumber.divide(bigRadix);
        }
        for (; !stack.isEmpty(); ) {
            result.append(stack.pop());
        }
        return result.length() == 0 ? "0" : result.toString();
    }

    public static String radixToNum(String number, int radix) {
        if (radix == TEN) {
            return number;
        }

        char ch[] = number.toCharArray();
        int len = ch.length;

        BigInteger bigRadix = new BigInteger(radix + "");
        BigInteger result = new BigInteger("0");
        BigInteger base = new BigInteger("1");


        for (int i = len - 1; i >= 0; i--) {
            BigInteger index = new BigInteger(TARGET_STR.indexOf(ch[i]) + "");
            result = result.add(index.multiply(base));
            base = base.multiply(bigRadix);
        }

        return result.toString();
    }

    /**
     * 整数n进制转m进制<br>
     * 1.仅支持整数据进制转换<br>
     * 2.进制取值范围：(n,m) in [2,62]
     *
     * @param num
     * @param fromRadix
     * @param toRadix
     * @return
     */
    public static String transRadix(String num, int fromRadix, int toRadix) {
        return numToRadix(radixToNum(num, fromRadix), toRadix);
    }

    /**
     * 转62进制
     *
     * @param num
     * @param fromRadix
     * @return
     */
    public static String numToRadix62(String num, int fromRadix) {
        return numToRadix(radixToNum(num, fromRadix), 62);
    }

    /**
     * 转36进制--无大写字母，更适合用在不区分大小写的场景
     *
     * @param num
     * @param fromRadix
     * @return
     */
    public static String numToRadix36(String num, int fromRadix) {
        return numToRadix(radixToNum(num, fromRadix), 36);
    }

    /**
     * 按概率命中目标
     *
     * @param probability 命中概率，小数格式
     * @return
     */
    public static boolean hitWithProbability(BigDecimal probability) {
        return hitWithProbability(probability, probability.scale());
    }

    /**
     * 按概率命中目标
     *
     * @param probability 命中概率，小数格式
     * @param scale       精度，有效的小数位数
     * @return
     */
    public static boolean hitWithProbability(BigDecimal probability, Integer scale) {
        if (null == scale || scale <= 0) {
            scale = probability.scale();
        }
        //样本区间右极值
        int sampleRangeR = org.dromara.hutool.core.math.NumberUtil.pow(TEN, scale).intValue();
        //目标区间右极值--通过将概率放大为整数得到
        int targetRangeR = org.dromara.hutool.core.math.NumberUtil.mul(probability, sampleRangeR).intValue();
        //从区间[1,sampleRangeR]随机取一个数，如果它的值落在[1,targetRangeR]区间，则为命中
        int hit = new Random().nextInt(sampleRangeR) + 1;
        if (hit <= targetRangeR) {
            //命中
            return true;
        }
        return false;
    }

    /**
     * 对列表串某一段求和
     *
     * @param dataList     数据源
     * @param dataMapper   数据映射器
     * @param filter       过滤器
     * @param defaultValue 不存在数据时返回的默认值
     * @param <T>
     * @param <K>
     * @return
     */
    public static <T, K extends Number> BigDecimal calcSum(List<T> dataList, Function<T, K> dataMapper, Predicate<K> filter, BigDecimal defaultValue) {
        if (CheckUtil.isBlank(dataList)) {
            return defaultValue;
        }

        List<Number> list = dataList.stream()
                .map(it -> dataMapper.apply(it))
                .filter(it -> {
                    if (null != filter) {
                        return filter.test(it);
                    }
                    return true;
                })
                .collect(Collectors.toList());
        if (CheckUtil.isBlank(list)) {
            return defaultValue;
        }
        return NumberUtil.add(list.toArray(new Number[list.size()]));
    }

    /**
     * 对列表某一字段求平均值
     *
     * @param dataList     数据源
     * @param dataMapper   数据映射器
     * @param filter       过滤器
     * @param defaultValue 不存在数据时返回的默认值
     * @param <T>
     * @param <K>
     * @return
     */
    public static <T, K extends Number> BigDecimal calcAvg(List<T> dataList, Function<T, K> dataMapper, Predicate<K> filter, BigDecimal defaultValue) {
        if (CheckUtil.isBlank(dataList)) {
            return defaultValue;
        }

        List<Number> list = dataList.stream()
                .map(it -> dataMapper.apply(it))
                .filter(it -> {
                    if (null != filter) {
                        return filter.test(it);
                    }
                    return true;
                })
                .collect(Collectors.toList());
        if (CheckUtil.isBlank(list)) {
            return defaultValue;
        }
        return NumberUtil.div(NumberUtil.add(list.toArray(new Number[list.size()])), list.size());
    }

    /**
     * 对列表某一字段求最大值
     *
     * @param dataList     数据源
     * @param dataMapper   数据映射器
     * @param filter       过滤器
     * @param defaultValue 不存在数据时返回的默认值
     * @param <T>
     * @param <K>
     * @return
     */
    public static <T, K extends BigDecimal> BigDecimal calcMax(List<T> dataList, Function<T, K> dataMapper, Predicate<K> filter, BigDecimal defaultValue) {
        if (CheckUtil.isBlank(dataList)) {
            return defaultValue;
        }

        List<BigDecimal> list = dataList.stream()
                .map(it -> dataMapper.apply(it))
                .filter(it -> {
                    if (null != filter) {
                        return filter.test(it);
                    }
                    return true;
                })
                .collect(Collectors.toList());
        if (CheckUtil.isBlank(list)) {
            return defaultValue;
        }
        return NumberUtil.max(list.toArray(new BigDecimal[list.size()]));
    }

    public static <T extends Comparable<? super T>> T max(T... numberArray) {
        return ArrayUtil.max(numberArray);
    }

    public static <T extends Comparable<? super T>> T min(T... numberArray) {
        return ArrayUtil.min(numberArray);
    }

    static int randomCloseLR(int min, int max) {
        return (new Random()).nextInt(max - min + 1) + min;
    }

    public static void main(String[] args) {
//		String s = ObjectUtils.toString("AfDX");
//		System.out.println(s);
//		System.out.println(transRadix(s, 62, 36));

        BigDecimal probability = ObjectUtil.toBigDecimal(0.223);
        //样本区间右极值
        int sampleRangeR = org.dromara.hutool.core.math.NumberUtil.pow(TEN, probability.scale()).intValue();
        //目标区间右极值--通过将概率放大为整数得到
        int targeRangeR = org.dromara.hutool.core.math.NumberUtil.mul(probability, sampleRangeR).intValue();

        int c = 0;
        for (int i = 0; i < sampleRangeR; i++) {
            //从区间[1,rangeR]随机取一个数，如果它的值落在[1,targeRangeR]区间，则为命中
            int hit = new Random().nextInt(sampleRangeR) + 1;
            if (hit <= targeRangeR) {
                //命中
                c = c + 1;
                System.out.println(hit);
            }
        }
        System.out.println(c);

    }

}
